use bytes::{Buf, BufMut, Bytes, BytesMut};
use sha1::{Digest, Sha1};
use std::{
    fmt::Display,
    fs::{self, File},
    io::{Read, Write},
    net::{IpAddr, TcpStream},
    path::PathBuf,
    str::{FromStr, Matches},
    thread,
    time::Duration,
};

use crate::{
    ahoben::BencodeEntity,
    message::{Message, MessageType, RequestPayLoad},
    tormeta::TorMeta,
    tracker::Tracker,
    util::gen_client_id,
};

enum Event {
    Start,
    Completed,
    Stopped,
}
enum Stat {
    Init,
    Connected,
}
pub struct Peer {
    peer_id: Vec<u8>,
    ip_addr: IpAddr,
    port: u32,
    socket: Option<TcpStream>,
    stat: Stat,
}

impl Display for Peer {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{}: {}:{}",
            String::from_utf8_lossy(self.peer_id.as_slice()),
            self.ip_addr,
            self.port
        )
    }
}

impl Peer {
    pub fn handshake(&mut self, info_hash: &[u8], client_id: &[u8]) {
        let socket = TcpStream::connect((self.ip_addr, self.port as u16));
        if let Ok(mut socket) = socket {
            let mut buf = [0u8; 1024];
            println!("connect to peer... ");
            let prefix = b"\x13BitTorrent protocol\x00\x00\x00\x00\x00\x00\x00\x00";
            let pack: Vec<u8> = prefix[..]
                .iter()
                .chain(info_hash)
                .chain(client_id)
                .copied()
                .collect();
            socket.write(pack.as_slice()).unwrap();
            if let Ok(readed) = socket.read(&mut buf[..]) {
                println!("握手成功...");
                self.socket = Some(socket);
            } else {
                println!("握手失败...");
            }
        } else {
            let e = socket.unwrap_err();
            eprintln!("{}", e);
        }
    }

    fn wait_message(socket: &mut TcpStream, msg_type: MessageType) -> Bytes {
        let mut len = [0u8; 4];
        loop {
            let res = socket.read_exact(&mut len);
            let len: u32 = u32::from_be_bytes(len);
            if len == 0 || res.is_err() {
                // KeepAlive
                // let _ = socket.write(b"\x00\x00\x00\x00");
                // 等待
                println!("等待...");
                thread::sleep(Duration::from_millis(100));
            } else {
                let mut msg_body = BytesMut::with_capacity(len as usize);
                let mut curr_read: usize = 0;
                let mut buf = [0u8; 1024];
                while curr_read < len as usize {
                    let readed = socket.read(&mut buf).unwrap();
                    curr_read += readed;
                    msg_body.put_slice(&buf[0..readed]);
                }
                let m_type = msg_body.get_u8();
                if (msg_type as u8) == m_type {
                    return msg_body.into();
                }
            }
        }
    }

    pub fn request(&mut self, index: u32, offset: u32, length: u32) -> Option<Bytes> {
        if let Some(socket) = &mut self.socket {
            println!("发送req");
            let req_payload = RequestPayLoad::new(index, offset, length);
            let req_payload: Vec<u8> =
                Message::from_payload(crate::message::PayLoad::Request(req_payload)).into();
            socket.write(req_payload.as_slice()).unwrap();
            let piece = Peer::wait_message(socket, MessageType::Piece);
            //16384
            return Some(piece.slice(8..));
        }
        None
    }

    pub fn interested(&mut self) {
        if let Some(socket) = &mut self.socket {
            let mut buf = [0u8; 1024];
            println!("发送interested");
            let interested: Vec<u8> =
                Message::from_payload(crate::message::PayLoad::Interested).into();
            socket.write(interested.as_slice()).unwrap();
            buf.fill(0);
            let _ = socket.read(&mut buf);
        }
    }
    pub fn bitfield(&mut self, bits: Vec<u8>) {
        if let Some(socket) = &mut self.socket {
            let mut buf = [0u8; 1024];
            println!("发送bitfield");
            let bitfeild_message: Vec<u8> =
                Message::from_payload(crate::message::PayLoad::Bitfield(bits)).into();
            socket.write(bitfeild_message.as_slice()).unwrap();
            buf.fill(0);
        }
    }
}

pub struct Client {
    trackers: Vec<Tracker>,
    torrent: Option<TorMeta>,
    client_id: String,
    downloaded: usize,
    uploaded: usize,
    left: usize,
    event: Option<Event>,
    port: usize,
    peers_list: Option<Vec<Peer>>,
}

impl Client {
    fn decode_tracker_resp(&mut self, raw_bytes: Bytes) {
        let peers_resp = BencodeEntity::try_from(raw_bytes.as_ref()).unwrap();
        let mut peer_list = Vec::<Peer>::new();
        //fs::write("peers_resp.txt", peers_resp.to_string()).unwrap();
        if let BencodeEntity::Dict(d) = &peers_resp {
            if let Some(BencodeEntity::List(peers)) = d.get("peers") {
                for peer in peers {
                    if let BencodeEntity::Dict(peer_dict) = peer {
                        if let Some(BencodeEntity::String(ip)) = peer_dict.get("ip") {
                            if let Some(BencodeEntity::Integer(port)) = peer_dict.get("port") {
                                if let Some(BencodeEntity::String(peer_id)) =
                                    peer_dict.get("peer id")
                                {
                                    let p = Peer {
                                        ip_addr: IpAddr::from_str(&String::from_utf8_lossy(
                                            ip.as_slice(),
                                        ))
                                        .unwrap(),
                                        port: *port as u32,
                                        peer_id: peer_id.clone(),
                                        socket: None,
                                        stat: Stat::Init,
                                    };
                                    peer_list.push(p);
                                }
                            }
                        }
                    }
                }
            }
        }
        self.peers_list = Some(peer_list);
        self.peers_list_show();
    }

    fn peers_list_show(&self) {
        if let Some(peers) = &self.peers_list {
            println!("当前的可用peers: ");
            for peer in peers {
                println!("{}", peer)
            }
        } else {
            println!("当前无可用peers");
        }
    }

    fn download(&mut self) {
        let tor = self.torrent.as_ref().unwrap();
        let tracker = if self.trackers.len() > 0 {
            Tracker::new(&self.trackers.get(0).unwrap().announce())
        } else {
            Tracker::new(tor.get_announce())
        };
        println!("query tracker: {}", tracker.announce());
        if !tracker.announce().starts_with("http") {
            eprintln!("不支持的tracker协议");
            return;
        }
        self.fetch_peers(tracker);
        let files = &self.torrent.as_ref().unwrap().files();
        let file = files.get(0).unwrap();
        let file_name = file.name.clone();
        let mut remaind = file.size as u32;
        let mut sha1_core;
        let pices_count = self.torrent.as_ref().unwrap().get_chunks_count();
        let pices_length = self.torrent.as_ref().unwrap().get_piece_length();
        if let Ok(mut outfile) = File::options().append(true).create(true).open(file_name) {
            for index in 0..pices_count {
                let res = self
                    .download_pices(1, index, remaind.min(pices_length))
                    .unwrap();
                let downloaded_len = res.len() as u32;
                remaind -= downloaded_len;
                sha1_core = Sha1::new();
                sha1_core.update(res.as_ref());
                let local_hash = self
                    .torrent
                    .as_ref()
                    .unwrap()
                    .get_chunk_hash(index as usize)
                    .unwrap();
                if local_hash == sha1_core.finalize().as_slice() {
                    println!("chunk {index} Hash 正确！！！");
                    let _ = outfile.write_all(&res);
                }
            }
        }
    }

    pub fn download_pices(
        &mut self,
        peer_idx: usize,
        index: u32,
        piece_length: u32,
    ) -> Option<Bytes> {
        if let Some(peer) = self.peers_list.as_mut().unwrap().get_mut(peer_idx) {
            if let Stat::Init = &peer.stat {
                peer.handshake(
                    self.torrent.as_ref().unwrap().get_info_hash(),
                    self.client_id.as_bytes(),
                );
                peer.interested();
                peer.stat = Stat::Connected;
            }
            let mut remaind = piece_length;
            let mut offset: u32 = 0;
            let mut pices_bytes = BytesMut::with_capacity(piece_length as usize);
            while remaind > 0 {
                let download_len: u32 = if remaind > 16384 { 16384 } else { remaind };

                let raw_download_bytes = peer.request(index, offset, download_len);
                if let Some(raw_download_bytes) = raw_download_bytes {
                    if raw_download_bytes.len() == 0 {
                        println!("等于零。。。");
                        break;
                    }
                    offset += raw_download_bytes.len() as u32;
                    remaind -= raw_download_bytes.len() as u32;
                    pices_bytes.put_slice(&raw_download_bytes);
                }
            }
            Some(pices_bytes.into())
        } else {
            None
        }
    }

    pub fn set_file(&mut self, tor: TorMeta) {
        self.left += tor.get_size();
        self.torrent = Some(tor);
    }

    fn update_left(&mut self) {}

    pub fn start(&mut self) {
        self.download();
    }

    pub fn fetch_peers(&mut self, tracker: Tracker) {
        let client = reqwest::blocking::Client::new();
        let mut url = String::new();
        url.push_str(tracker.announce());
        // info_hash 是对sha1得到的bytes直接进行urlencode编码，不要进行任何的字符串操作
        url.push_str("?info_hash=");
        let info_hash = self
            .torrent
            .as_ref()
            .unwrap()
            .get_info_hash()
            .iter()
            .map(|b| format!("%{:02X}", b))
            .collect::<Vec<String>>()
            .join("");
        url.push_str(&info_hash);
        url.push_str("&");
        url.push_str(
            &serde_urlencoded::to_string(&[
                ("peer_id", self.client_id.as_str()),
                ("port", self.port.to_string().as_str()),
                ("uploaded", self.uploaded.to_string().as_str()),
                ("downloaded", self.downloaded.to_string().as_str()),
                ("left", self.left.to_string().as_str()),
            ])
            .unwrap(),
        );
        println!("url: {}", url);
        let raw_bytes = client.get(url).send().unwrap().bytes().unwrap();
        self.decode_tracker_resp(raw_bytes);
    }
}

pub struct ClientBuilder {
    tracker_list: Vec<String>,
}

impl ClientBuilder {
    pub fn new() -> Self {
        Self {
            tracker_list: Vec::new(),
        }
    }
    pub fn trackers(mut self, tracker_list: Vec<&str>) -> Self {
        self.tracker_list
            .push(tracker_list.iter().map(|s| s.to_owned()).collect());
        self
    }
    pub fn build(self) -> Client {
        let tracers = self
            .tracker_list
            .iter()
            .map(|ann| Tracker::new(&ann))
            .collect();
        let client_id = gen_client_id();
        Client {
            torrent: None,
            trackers: tracers,
            client_id,
            downloaded: 0,
            uploaded: 0,
            left: 0,
            event: None,
            port: 6882,
            peers_list: None,
        }
    }
}

#[cfg(test)]
mod test {
    use crate::tormeta::TorMeta;

    use super::ClientBuilder;

    #[test]
    fn test_client1() {
        let mut client = ClientBuilder::new().build();
        let tor = TorMeta::from("sample.torrent".into()).unwrap();
        client.set_file(tor);
        client.start();
    }
}
